package com.excesys.exsecs.access.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeNode;
import cn.hutool.core.lang.tree.TreeUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.excesys.exsecs.access.dto.AccessSubareaDTO;
import com.excesys.exsecs.access.entity.AccessBuildings;
import com.excesys.exsecs.access.entity.AccessFloor;
import com.excesys.exsecs.access.entity.AccessSubarea;
import com.excesys.exsecs.access.mapper.AccessBuildingsMapper;
import com.excesys.exsecs.access.mapper.AccessFloorMapper;
import com.excesys.exsecs.access.mapper.AccessSubareaMapper;
import com.excesys.exsecs.access.service.AccessSubareaService;
import com.excesys.exsecs.access.vo.AccessSubareaExcelVO;
import com.excesys.exsecs.access.vo.AccessSubareaVO;
import com.excesys.exsecs.common.core.util.R;
import com.excesys.exsecs.common.excel.vo.ErrorMessage;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.validation.BindingResult;

import javax.validation.constraints.NotNull;
import java.text.DecimalFormat;
import java.text.Format;
import java.text.ParseException;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 区域管理-空间分区
 *
 * @author lxc
 * @date 2024-09-13 14:26:53
 */
@Service
@RequiredArgsConstructor
public class AccessSubareaServiceImpl extends ServiceImpl<AccessSubareaMapper, AccessSubarea> implements AccessSubareaService {

    private final AccessFloorMapper accessFloorMapper;

    private final AccessBuildingsMapper accessBuildingsMapper;

    /**
     * 构建树查询 1. 不是懒加载情况，查询全部 2. 是懒加载，根据parentId 查询 2.1 父节点为空，则查询ID -1
     * @param lazy 是否是懒加载
     * @param parentId 父节点ID
     * @return
     */
    @Override
    public List<Tree<String>> treeMenu(boolean lazy, String parentId) {
        // 是否懒加载
        if (!lazy) {
            // 1、父级节点从BUILDING中添加。
            List<TreeNode<String>> collectbulding = accessBuildingsMapper
                    .selectList(Wrappers.<AccessBuildings>lambdaQuery().orderByAsc(AccessBuildings::getUpdateTime)).stream()
                    .map(getNodeFunctionB()).collect(Collectors.toList());
            // 2、二级节点从楼层信息里添加。
            List<TreeNode<String>> collectfloor = accessFloorMapper
                    .selectList(Wrappers.<AccessFloor>lambdaQuery().orderByAsc(AccessFloor::getSort)).stream()
                    .map(getNodeFunctionF()).collect(Collectors.toList());
            // 3、三级节点从分区中提取加入。isNull为父级分区
            List<TreeNode<String>> collectarea = baseMapper
                    .selectList(Wrappers.<AccessSubarea>lambdaQuery().isNull(AccessSubarea::getSubareadCode)
                            .orderByAsc(AccessSubarea::getSort))
                    .stream().map(getNodeFunctionA()).collect(Collectors.toList());
            // 4、四级内部分区
            List<TreeNode<String>> collectaread = baseMapper
                    .selectList(Wrappers.<AccessSubarea>lambdaQuery().isNotNull(AccessSubarea::getSubareadCode)
                            .orderByAsc(AccessSubarea::getSort))
                    .stream().map(getNodeFunctionAD()).collect(Collectors.toList());
            // 4、后续级别节点递归加入树状节点中。
            List<TreeNode<String>> collect = mergeList(collectbulding, collectfloor, collectarea, collectaread);

            return TreeUtil.build(collect, "-1");
        }
        // Long parent = parentId == null ? CommonConstants.MENU_TREE_ROOT_ID : parentId;
        List<TreeNode<String>> collect = new ArrayList<>();
        String pid = "";
        return TreeUtil.build(collect, pid);

    }

    @Override
    public List<Tree<String>> treeMenu2(boolean lazy, String parentId) {
        if (!lazy) {
            // 3、三级节点从分区中提取加入。isNull为父级分区
            List<TreeNode<String>> collectarea = baseMapper
                    .selectList(Wrappers.<AccessSubarea>lambdaQuery().isNull(AccessSubarea::getSubareadCode)
                            .orderByAsc(AccessSubarea::getSort))
                    .stream().map(getNodeFunctionAR()).collect(Collectors.toList());
            // 4、四级内部分区
            List<TreeNode<String>> collectaread = baseMapper
                    .selectList(Wrappers.<AccessSubarea>lambdaQuery().isNotNull(AccessSubarea::getSubareadCode)
                            .orderByAsc(AccessSubarea::getSort))
                    .stream().map(getNodeFunctionAD()).collect(Collectors.toList());
            // 4、后续级别节点递归加入树状节点中。
            List<TreeNode<String>> collect = mergeList(collectarea, collectaread);
            return TreeUtil.build(collect, "-1");
            // Long parent = parentId == null ? CommonConstants.MENU_TREE_ROOT_ID : parentId;
        }
        List<TreeNode<String>> collect = new ArrayList<>();
        String pid = "";
        return TreeUtil.build(collect, pid);
    }
    @Override
    public R importSubarea(List<AccessSubareaExcelVO> excelVOList, BindingResult bindingResult)
            throws ParseException {
        // 通用校验获取失败的数据
        List<ErrorMessage> errorMessageList = (List<ErrorMessage>) bindingResult.getTarget();
        // 个性化校验逻辑
        List<AccessSubarea> bamSSubareas = this.list();

        int sucess = 0;
        int fail = 0;
        // 执行数据插入操作 组装 RoleDto
        for (AccessSubareaExcelVO excel : excelVOList) {

            Set<String> errorMsg = new HashSet<>();

            boolean existSSubarea = bamSSubareas.stream()
                    .anyMatch(subarea -> excel.getSubareaCode().equals(subarea.getSubareaCode()));
            if (existSSubarea) {
                errorMsg.add("分区：" + excel.getSubareaCode() + "已经存在。");
            }
            // 数据合法情况
            if (CollUtil.isEmpty(errorMsg)) {
            }
            else {
                // 数据不合法情况
                errorMessageList.add(new ErrorMessage(excel.getLineNum(), errorMsg));
            }
            int num = insertSubarea(excel);
            if (num == 1) {
                fail++;
            }
            else {
                sucess++;
            }
        }
        if (CollUtil.isNotEmpty(errorMessageList)) {
            return R.failed(errorMessageList);
        }
        return R.ok(sucess, "成功导入数量" + sucess + "。" + "存在" + fail + "重复");
    }

    /**
     * @description: TODO 批量导入子分区 @param @return @throws @author yuan quan @date 2023/5/30
     * 14:35
     */
    @Override
    public R batchImportSubarea(List<AccessSubareaExcelVO> excelVOList, BindingResult bindingResult,
                                    String subareadCode) throws ParseException {
        // 通用校验获取失败的数据
        List<ErrorMessage> errorMessageList = (List<ErrorMessage>) bindingResult.getTarget();
        // 个性化校验逻辑
        // List<BamSSubarea> bamSSubareas = this.list();
        int sucess = 0;
        int fail = 0;
        // 执行数据插入操作 组装 RoleDto
        int i = 1;
        for (AccessSubareaExcelVO excel : excelVOList) {
            Set<String> errorMsg = new HashSet<>();
            // boolean existSSubarea = bamSSubareas.stream()
            // .anyMatch(subarea ->
            // excel.getSubareaCode().equals(subarea.getSubareaCode()));
            // if (existSSubarea) {
            // errorMsg.add("分区："+excel.getSubareaCode()+"已经存在。");
            // }
            // 数据合法情况
            if (CollUtil.isEmpty(errorMsg)) {
            }
            else {
                // 数据不合法情况
                errorMessageList.add(new ErrorMessage(excel.getLineNum(), errorMsg));
            }
            int num = 0;
            if (null != subareadCode) {
                num = insertBatchSubarea(excel, subareadCode, i);
                i++;
            }
            if (num == 1) {
                fail++;
            }
            else {
                sucess++;
            }
        }
        if (CollUtil.isNotEmpty(errorMessageList)) {
            return R.failed(errorMessageList);
        }
        return R.ok(sucess, "成功导入数量" + sucess + "。" + "存在" + fail + "重复");
    }

    private int insertBatchSubarea(AccessSubareaExcelVO excel, String subareadCode, int i) throws ParseException {
        AccessSubarea bamSSubarea = new AccessSubarea();
        bamSSubarea.setSubareaCode(GetSubareaCodeBy(subareadCode, 3, ""));
        bamSSubarea.setRemark(excel.getRemark());
        bamSSubarea.setSubareaCn(excel.getSubareaCn());
        bamSSubarea.setSubareaEn(excel.getSubareaEn());
        bamSSubarea.setSort(i);
        // 建筑编码按照条件生成
        String jzbm = subareadCode.substring(0, 10);
        // 楼层编码按照条件生成
        String lc = subareadCode.substring(0, 14);
        bamSSubarea.setSubareaJzbm(jzbm);
        bamSSubarea.setSubareaLc(lc);
        bamSSubarea.setSubareadCode(subareadCode);
        boolean f = this.save(bamSSubarea);
        if (f) {
            return 0;
        }
        else {
            return 1;
        }

    }

    private int insertSubarea(AccessSubareaExcelVO excel) throws ParseException {
        AccessSubarea bamSSubarea = new AccessSubarea();
        List<AccessSubarea> bamSSubareas = baseMapper.selectList(
                Wrappers.<AccessSubarea>lambdaQuery().eq(AccessSubarea::getSubareaCode, excel.getSubareaCode()));
        if (bamSSubareas.size() <= 0) {
            if (null != excel.getSubareaLc()) {
                if (null != excel.getSubareaCode()) {
                    if (!excel.getSubareaCode().equals("AUTOINTSERT")) {
                        bamSSubarea.setSubareaCode(excel.getSubareaCode());
                    }
                    else {
                        bamSSubarea.setSubareaCode(GetSubareaCodeBy(excel.getSubareaLc(), 4, ""));
                    }
                }
                else {
                    bamSSubarea.setSubareaCode(GetSubareaCodeBy(excel.getSubareaLc(), 4, ""));
                }
                bamSSubarea.setRemark(excel.getRemark());
                bamSSubarea.setSubareaCn(excel.getSubareaCn());
                bamSSubarea.setSubareaEn(excel.getSubareaEn());
                bamSSubarea.setSubareaJzbm(excel.getSubareaJzbm());
                bamSSubarea.setSubareaLc(excel.getSubareaLc());
                String codes = excel.getSubareaCode().replace(excel.getSubareaLc(), "");
                // 如果长度为4 则表示为父级分区 否则为子级分区
                if (codes.length() > 4) {
                    String pcode = excel.getSubareaCode().substring(0, excel.getSubareaCode().length() - 3);
                    if (!excel.getSubareaCode().equals("AUTOINTSERT")) {
                        bamSSubarea.setSubareadCode(pcode);
                    }
                }
                this.save(bamSSubarea);
                return 0;
            }
            else {
                return 1;
            }
        }
        else {
            return 1;
        }
    }

    @Override
    public List<AccessSubareaExcelVO> listSubarea(AccessSubareaDTO bamSSubarea) {
        // List<BamSSubarea> bamSSubareas = this.list(Wrappers.emptyWrapper());
        QueryWrapper<AccessSubareaDTO> query = WrapperSubArea(bamSSubarea);
        List<AccessSubareaVO> bamSSubareas = baseMapper.selectBamSSubareaVos(query);
        // 转换成execl 对象输出
        return bamSSubareas.stream().map(subarea -> {
            AccessSubareaExcelVO bamSSubareaExcelVO = new AccessSubareaExcelVO();
            BeanUtil.copyProperties(subarea, bamSSubareaExcelVO);
            // bamSSubareaExcelVO.setSubareaCoords(subarea.getSubareaCoords());
            return bamSSubareaExcelVO;
        }).collect(Collectors.toList());
    }

    @Override
    public IPage<AccessSubareaVO> getSubareaVosPage(Page page, AccessSubareaDTO bamSSubareaDTO) {
        QueryWrapper<AccessSubareaDTO> query = WrapperSubArea(bamSSubareaDTO);
        return baseMapper.selectBamSSubareaByPages(page, query);
    }

    @Override
    public List<AccessSubareaVO> getSubareaVos(AccessSubareaDTO bamSSubareaDTO) {
        QueryWrapper<AccessSubareaDTO> query = WrapperSubArea(bamSSubareaDTO);
        return baseMapper.selectBamSSubareaVos(query);
    }

    @Override
    public void addSubAreaByMapJson(String data, String lc) throws ParseException {
        JSONObject body = JSONUtil.parseObj(data);
        body.getStr("type");
        // 取得features集合
        JSONArray features = (JSONArray) body.get("features");
        // 根据楼层编码取得分区编码规则
        List<AccessSubarea> bamSSubareas = baseMapper.selectList(Wrappers.<AccessSubarea>lambdaQuery()
                .eq(AccessSubarea::getSubareaLc, lc).orderByDesc(AccessSubarea::getUpdateTime));
        String fl = lc;
        String code = "0000";
        String fccode = "";
        if (bamSSubareas.size() > 0) {
            String scode = bamSSubareas.get(0).getSubareaCode().replace(lc, "");
            Format f = new DecimalFormat("0000");
            Long ij = (Long) f.parseObject(scode);
            ij = ij + 1;
            fccode = lc + f.format(ij);
        }
        else {
            fccode = fl + code;
        }
        // 取到 每个分区详情信息
        int i = 1;
        for (Object object : features) {
            JSONObject jsonObject = (JSONObject) object;
            JSONObject jpo = JSONUtil.parseObj(jsonObject.get("properties").toString());
            JSONObject geo = JSONUtil.parseObj(jsonObject.get("geometry").toString());
            AccessSubarea subarea = new AccessSubarea();
            String fqm = jpo.getStr("des");
            String fqen = jsonObject.get("geometry_name").toString();
            JSONArray coords = (JSONArray) geo.get("coordinates");
            String coordstr = JSONUtil.toJsonStr(coords);
            // String coordstr=StringUtils.join(coords);
            subarea.setSubareaCn(fqm);
            subarea.setSubareaEn(fqen);
            subarea.setSubareaJzbm("3701030101");
            subarea.setSubareaLc(lc);
            subarea.setSort(i);
            subarea.setSubareaCode(fccode);
            i++;
            // 先清除 已存在的数据再进行更新
            baseMapper.delete(Wrappers.<AccessSubarea>lambdaQuery().eq(AccessSubarea::getSubareaCode, fccode));
            baseMapper.insert(subarea);

            String scode = fccode.replace(lc, "");
            Format f = new DecimalFormat("0000");
            Long ij = (Long) f.parseObject(scode);
            ij = ij + 1;
            fccode = lc + f.format(ij);
        }
    }

    @Override
    public String GetSubareaCodeBy(String code, int level, String suffix) throws ParseException {
        StringBuffer fc = new StringBuffer();
        for (int i = 0; i < level; i++) {
            // fc.concat("0");
            fc.append("0");
        }
        String fccode = "";
        String fl = code;
        if (StringUtils.isNotBlank(suffix)) {
            fl = code + suffix;
        }
        List<AccessSubarea> subareas = new ArrayList<>();
        if (code.length() <= 14) {
            subareas = baseMapper.selectList(Wrappers.<AccessSubarea>lambdaQuery().like(AccessSubarea::getSubareaCode, fl)
                    .isNull(AccessSubarea::getSubareadCode).orderByDesc(AccessSubarea::getSubareaCode));
        }
        else {
            subareas = baseMapper.selectList(Wrappers.<AccessSubarea>lambdaQuery().like(AccessSubarea::getSubareaCode, fl)
                    .orderByDesc(AccessSubarea::getSubareaCode));
        }
        if (subareas.size() > 0) {
            String scode = subareas.get(0).getSubareaCode().replace(code, "");
            scode = scode.replace(suffix, "");
            if (StringUtils.isEmpty(scode)) {
                Format f = new DecimalFormat(fc.toString());
                Long ij = (Long) f.parseObject(fc.toString());
                ij = ij + 1;
                fccode = fl + f.format(ij);
            }
            else {
                Format f = new DecimalFormat(fc.toString());
                Long ij = (Long) f.parseObject(scode);
                ij = ij + 1;
                fccode = fl + f.format(ij);
            }
        }
        else {
            Format f = new DecimalFormat(fc.toString());
            Long ij = (Long) f.parseObject(fc.toString());
            ij = ij + 1;
            fccode = fl + f.format(ij);
        }
        return fccode;
    }

    @Override
    public List<AccessSubareaVO> selectAll() {
        return this.baseMapper.selectAll();
    }

    @Override
    public AccessSubareaVO selectByPrimaryKey(String id) {
        return this.baseMapper.selectByPrimaryKey(id);
    }

    private QueryWrapper<AccessSubareaDTO> WrapperSubArea(AccessSubareaDTO bamSSubareaDTO) {
        QueryWrapper<AccessSubareaDTO> query = new QueryWrapper<>();
        if (ObjectUtil.isNotNull(bamSSubareaDTO.getSubareaCn())) {
            query.and(e -> {
                e.like("bama.subarea_cn", bamSSubareaDTO.getSubareaCn());
            });
        }
        if (ObjectUtil.isNotNull(bamSSubareaDTO.getSubareaCode())) {
            query.and(e -> {
                e.like("bama.subarea_code", bamSSubareaDTO.getSubareaCode());
            });
        }
        if (ObjectUtil.isNotNull(bamSSubareaDTO.getSubareaEn())) {
            query.and(e -> {
                e.like("bama.subarea_en", bamSSubareaDTO.getSubareaEn());
            });
        }
        if (ObjectUtil.isNotNull(bamSSubareaDTO.getSubareaLc())) {
            query.and(e -> {
                e.like("bama.subarea_lc", bamSSubareaDTO.getSubareaLc());
            });
        }
        if (ObjectUtil.isNotNull(bamSSubareaDTO.getSubareaJzbm())) {
            query.and(e -> {
                e.like("bama.subarea_jzbm", bamSSubareaDTO.getSubareaJzbm());
            });
        }
        if (ObjectUtil.isNotNull(bamSSubareaDTO.getSubareadCode())) {
            query.and(e -> {
                e.eq("bama.subaread_code", bamSSubareaDTO.getSubareadCode());
            });
        }
        else {
            query.and(e -> {
                e.isNull("bama.subaread_code");
            });
        }
        query.orderByAsc("bama.subarea_lc");
        return query;
    }

    @NotNull
    private Function<AccessSubarea, TreeNode<Long>> getNodeFunction() {
        return subarea -> {
            TreeNode<Long> node = new TreeNode<>();
            return node;
        };
    }

    /**
     * @description: TODO 建筑作为父节点 树状节点均为-1 @param @return @throws @author yuan quan @date
     * 2023/2/22 10:18
     */
    @NotNull
    private Function<AccessBuildings, TreeNode<String>> getNodeFunctionB() {
        return buildings -> {
            TreeNode<String> node = new TreeNode<>();
            node.setId(buildings.getBuildingsId());
            node.setName(buildings.getBuildingsCn());
            node.setParentId("-1");
            // node.setWeight(buildings.getUpdateTime());
            return node;
        };
    }

    @NotNull
    private Function<AccessFloor, TreeNode<String>> getNodeFunctionF() {
        return floor -> {
            TreeNode<String> node = new TreeNode<>();
            node.setId(floor.getFloorCode());
            node.setName(floor.getFloorCn());
            // node.setWeight(floor.getDescribe());
            node.setParentId(floor.getFloorPid());
            return node;
        };
    }

    @NotNull
    private Function<AccessSubarea, TreeNode<String>> getNodeFunctionA() {
        return subarea -> {
            TreeNode<String> node = new TreeNode<>();
            node.setId(subarea.getSubareaCode());
            node.setName(subarea.getSubareaCn());
            node.setParentId(subarea.getSubareaLc());
            return node;
        };
    }
    @NotNull
    private Function<AccessSubarea, TreeNode<String>> getNodeFunctionAR() {
        return subarea -> {
            TreeNode<String> node = new TreeNode<>();
            node.setId(subarea.getSubareaCode());
            node.setName(subarea.getSubareaCn());
            node.setParentId("-1");
            return node;
        };
    }

    @NotNull
    private Function<AccessSubarea, TreeNode<String>> getNodeFunctionAD() {
        return subarea -> {
            TreeNode<String> node = new TreeNode<>();
            node.setId(subarea.getSubareaCode());
            node.setName(subarea.getSubareaCn());
            node.setParentId(subarea.getSubareadCode());
            return node;
        };
    }

    public static <T> List<T> mergeList(List<T>... list) {
        List<T> res = new ArrayList<>();
        if (Objects.nonNull(list)) {
            for (List<T> l : list) {
                res.addAll(l);
            }
        }
        return res;
    }
}